package com.xzzz.irda.guardcat.server.metric.memory;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.xzzz.common.base.util.okhttp.HttpUtil;
import com.xzzz.common.base.util.json.JsonUtil;
import com.xzzz.irda.guardcat.core.constants.GuardcatConstant;
import com.xzzz.irda.guardcat.server.app.pojo.MachineInfo;
import com.xzzz.irda.guardcat.server.metric.MetricFetchJob;
import com.xzzz.irda.guardcat.server.metric.MetricRepository;
import com.xzzz.irda.guardcat.server.metric.Repository;
import com.xzzz.irda.guardcat.server.repos.mysql.JmmRepositoryInMySQL;
import lombok.extern.slf4j.Slf4j;

/**
 * @author xzzz
 */
@Slf4j
@Repository(repo = JmmRepositoryInMySQL.class)
public class JmmFetchJob extends MetricFetchJob<JmmInfo> {

    public JmmFetchJob(MachineInfo machineInfo, MetricRepository<JmmInfo> metricRepository) {
        super(machineInfo, metricRepository);
    }

    @Override
    public void run() {
        String url;
        long start = System.currentTimeMillis();
        String body = null;

        try {
            url = buildUrl(GuardcatConstant.API.METRIC_JVM_JMM);
            body = HttpUtil.get(url);
        } catch (Exception e) {
            long end = System.currentTimeMillis() - start;
            log.error("[GUARDCAT] 从客户端拉取 JMM ({}.{}.{}) 失败: {}, 用时:{}",
                    this.machine.getAppName(),
                    this.machine.getMachineId(),
                    this.machine.getIp(),
                    e.getMessage(),
                    end);
        }

        try {
            if (StrUtil.isNotBlank(body)) {
                JsonNode memoryInfo = JsonUtil.toJsonNode(body);

                JmmInfo memory = new JmmInfo();

                /*
                TODO
                客户端返回时间戳更加准确，服务端保存时间戳可能会出现顺序错误，例如两次拉取指标的间隔较小时，前一次拉取可能因为某些原因
                阻塞，导致前一次拉取在后一次拉取之后保存了数据，那么就会出现指标错误的情况。
                 */
                JmmInfo.UsageInfo heapUsage = getUsageInfo(memoryInfo, "heapUsage");
                JmmInfo.UsageInfo nonHeapUsage = getUsageInfo(memoryInfo, "nonHeapUsage");
                JmmInfo.UsageInfo codeCacheUsage = getUsageInfo(memoryInfo, "codeCacheUsage");
                JmmInfo.UsageInfo metaspaceUsage = getUsageInfo(memoryInfo, "metaspaceUsage");
                JmmInfo.UsageInfo compressedClassSpace = getUsageInfo(memoryInfo, "compressedClassSpace");
                JmmInfo.UsageInfo edenSpace = getUsageInfo(memoryInfo, "edenSpace");
                JmmInfo.UsageInfo survivorSpace = getUsageInfo(memoryInfo, "survivorSpace");
                JmmInfo.UsageInfo oldGen = getUsageInfo(memoryInfo, "oldGen");

                memory.setHeapUsage(heapUsage);
                memory.setNonHeapUsage(nonHeapUsage);
                memory.setCodeCacheUsage(codeCacheUsage);
                memory.setMetaspaceUsage(metaspaceUsage);
                memory.setCompressedClassSpace(compressedClassSpace);
                memory.setEdenSpace(edenSpace);
                memory.setSurvivorSpace(survivorSpace);
                memory.setOldGen(oldGen);
                memory.setCreTime(DateUtil.now());
                this.saveAndPublish(memory);
            }
        } catch (Exception e) {
            log.error("[GUARDCAT] 持久化 JMM 信息异常: {}", e.getMessage());
        }
    }

    private JmmInfo.UsageInfo getUsageInfo(JsonNode memoryInfo, String name) {
        return JsonUtil.toObj(memoryInfo.get(name).get("usageInfo").toString(), JmmInfo.UsageInfo.class);
    }
}
